home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume17 / screen2 / part02 < prev    next >
Encoding:
Internet Message Format  |  1989-02-06  |  59.2 KB

  1. Subject:  v17i083:  Screen, multiple windows on a CRT, Part02/02
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4.  
  5. Submitted-by: "Oliver Laumann" <unido!tub!net@uunet.UU.NET>
  6. Posting-number: Volume 17, Issue 83
  7. Archive-name: screen2/part02
  8.  
  9. [ Yes, the subject line is misleading, you could run this on your Blit.  --r$ ]
  10.  
  11. #! /bin/sh
  12. # This is a shell archive, meaning:
  13. # 1. Remove everything above the #! /bin/sh line.
  14. # 2. Save the resulting text in a file.
  15. # 3. Execute the file with /bin/sh (not csh) to create the files:
  16. #    screen.c
  17. #    screen.1
  18. export PATH; PATH=/bin:$PATH
  19. echo shar: extracting "'screen.c'" '(38140 characters)'
  20. if test -f 'screen.c'
  21. then
  22.     echo shar: will not over-write existing file "'screen.c'"
  23. else
  24. cat << \SHAR_EOF > 'screen.c'
  25. /* Copyright (c) 1987,1988 Oliver Laumann, Technical University of Berlin.
  26.  * Not derived from licensed software.
  27.  *
  28.  * Permission is granted to freely use, copy, modify, and redistribute
  29.  * this software, provided that no attempt is made to gain profit from it,
  30.  * the author is not construed to be liable for any results of using the
  31.  * software, alterations are clearly marked as such, and this notice is
  32.  * not modified.
  33.  */
  34.  
  35. static char ScreenVersion[] = "screen 2.0a 19-Oct-88";
  36.  
  37. #include <stdio.h>
  38. #include <sgtty.h>
  39. #include <signal.h>
  40. #include <errno.h>
  41. #include <ctype.h>
  42. #include <utmp.h>
  43. #include <pwd.h>
  44. #include <nlist.h>
  45. #include <fcntl.h>
  46. #include <sys/types.h>
  47. #include <sys/time.h>
  48. #include <sys/file.h>
  49. #include <sys/wait.h>
  50. #include <sys/socket.h>
  51. #include <sys/un.h>
  52. #include <sys/stat.h>
  53. #include <sys/dir.h>
  54. #ifdef SUNLOADAV
  55. #include <sys/param.h>
  56. #endif
  57. #include "screen.h"
  58.  
  59. #ifdef GETTTYENT
  60. #   include <ttyent.h>
  61. #else
  62.     static struct ttyent {
  63.     char *ty_name;
  64.     } *getttyent();
  65.     static char *tt, *ttnext;
  66.     static char ttys[] = "/etc/ttys";
  67. #endif
  68.  
  69. #define MAXWIN     10
  70. #define MSGWAIT     5
  71.  
  72. #define Ctrl(c) ((c)&037)
  73.  
  74. extern char *blank, Term[], **environ;
  75. extern rows, cols;
  76. extern ISO2022;
  77. extern status;
  78. extern time_t TimeDisplayed;
  79. extern char AnsiVersion[];
  80. extern short ospeed;
  81. extern flowctl;
  82. extern errno;
  83. extern sys_nerr;
  84. extern char *sys_errlist[];
  85. extern char *index(), *rindex(), *malloc(), *getenv(), *MakeTermcap();
  86. extern char *getlogin(), *ttyname();
  87. static AttacherFinit(), Finit(), SigHup(), SigChld();
  88. static char *MakeBellMsg(), *Filename(), **SaveArgs(), *GetTtyName();
  89.  
  90. static char PtyName[32], TtyName[32];
  91. static char *ShellProg;
  92. static char *ShellArgs[2];
  93. static char inbuf[IOSIZE];
  94. static inlen;
  95. static ESCseen;
  96. static GotSignal;
  97. static char DefaultShell[] = "/bin/sh";
  98. static char DefaultPath[] = ":/usr/ucb:/bin:/usr/bin";
  99. static char PtyProto[] = "/dev/ptyXY";
  100. static char TtyProto[] = "/dev/ttyXY";
  101. static int TtyMode = 0622;
  102. static char SockPath[512];
  103. static char SockDir[] = ".screen";
  104. static char *SockNamePtr, *SockName;
  105. static ServerSocket;
  106. static char *NewEnv[MAXARGS];
  107. static char Esc = Ctrl('a');
  108. static char MetaEsc = 'a';
  109. static char *home;
  110. static HasWindow;
  111. static utmp, utmpf;
  112. static char UtmpName[] = "/etc/utmp";
  113. static char *LoginName;
  114. static char *BellString = "Bell in window %";
  115. static mflag, nflag, fflag, rflag;
  116. static char HostName[MAXSTR];
  117. static Detached;
  118. static AttacherPid;    /* Non-Zero in child if we have an attacher */
  119. static DevTty;
  120. #ifdef LOADAV
  121.     static char KmemName[] = "/dev/kmem";
  122. #ifdef sequent
  123.     static char UnixName[] = "/dynix";
  124. #else
  125.     static char UnixName[] = "/vmunix";
  126. #endif
  127. #ifdef alliant
  128.     static char AvenrunSym[] = "_Loadavg";
  129. #else
  130.     static char AvenrunSym[] = "_avenrun";
  131. #endif
  132.     static struct nlist nl[2];
  133.     static avenrun, kmemf;
  134. #ifdef SUNLOADAV
  135.     long loadav[3];
  136. #else
  137. #ifdef alliant
  138.     long loadav[4];
  139. #else
  140.     double loadav[3];
  141. #endif
  142. #endif
  143.  
  144. #endif
  145.  
  146. struct mode {
  147.     struct sgttyb m_ttyb;
  148.     struct tchars m_tchars;
  149.     struct ltchars m_ltchars;
  150.     int m_ldisc;
  151.     int m_lmode;
  152. } OldMode, NewMode;
  153.  
  154. static struct win *curr, *other;
  155. static CurrNum, OtherNum;
  156. static struct win *wtab[MAXWIN];
  157.  
  158. #define KEY_IGNORE         0
  159. #define KEY_HARDCOPY       1
  160. #define KEY_SUSPEND        2
  161. #define KEY_SHELL          3
  162. #define KEY_NEXT           4
  163. #define KEY_PREV           5
  164. #define KEY_KILL           6
  165. #define KEY_REDISPLAY      7
  166. #define KEY_WINDOWS        8
  167. #define KEY_VERSION        9
  168. #define KEY_OTHER         10
  169. #define KEY_0             11
  170. #define KEY_1             12
  171. #define KEY_2             13
  172. #define KEY_3             14
  173. #define KEY_4             15
  174. #define KEY_5             16
  175. #define KEY_6             17
  176. #define KEY_7             18
  177. #define KEY_8             19
  178. #define KEY_9             20
  179. #define KEY_XON           21
  180. #define KEY_XOFF          22
  181. #define KEY_INFO          23
  182. #define KEY_TERMCAP       24
  183. #define KEY_QUIT          25
  184. #define KEY_DETACH        26
  185. #define KEY_CREATE        27
  186.  
  187. struct key {
  188.     int type;
  189.     char **args;
  190. } ktab[256];
  191.  
  192. char *KeyNames[] = {
  193.     "hardcopy", "suspend", "shell", "next", "prev", "kill", "redisplay",
  194.     "windows", "version", "other", "select0", "select1", "select2", "select3",
  195.     "select4", "select5", "select6", "select7", "select8", "select9",
  196.     "xon", "xoff", "info", "termcap", "quit", "detach",
  197.     0
  198. };
  199.  
  200. main (ac, av) char **av; {
  201.     register n, len;
  202.     register struct win **pp, *p;
  203.     char *ap;
  204.     int s, r, w, x = 0;
  205.     int aflag = 0;
  206.     struct timeval tv;
  207.     time_t now;
  208.     char buf[IOSIZE], *myname = (ac == 0) ? "screen" : av[0];
  209.     char rc[256];
  210.     struct stat st;
  211.  
  212.     while (ac > 0) {
  213.     ap = *++av;
  214.     if (--ac > 0 && *ap == '-') {
  215.         switch (ap[1]) {
  216.         case 'c':        /* Compatibility with older versions. */
  217.         break;
  218.         case 'a':
  219.         aflag = 1;
  220.         break;
  221.         case 'm':
  222.         mflag = 1;
  223.         break;
  224.         case 'n':
  225.         nflag = 1;
  226.         break;
  227.         case 'f':
  228.         fflag = 1;
  229.         break;
  230.         case 'r':
  231.         rflag = 1;
  232.         if (ap[2]) {
  233.             SockName = ap+2;
  234.             if (ac != 1) goto help;
  235.         } else if (--ac == 1) {
  236.             SockName = *++av;
  237.         } else if (ac != 0) goto help;
  238.         break;
  239.         case 'e':
  240.         if (ap[2]) {
  241.             ap += 2;
  242.         } else {
  243.             if (--ac == 0) goto help;
  244.             ap = *++av;
  245.         }
  246.         if (strlen (ap) != 2)
  247.             Msg (0, "Two characters are required with -e option.");
  248.         Esc = ap[0];
  249.         MetaEsc = ap[1];
  250.         break;
  251.         default:
  252.         help:
  253.         Msg (0, "Use: %s [-a] [-f] [-n] [-e xy] [cmd args]\n\
  254.  or: %s -r [host.tty]", myname, myname);
  255.         }
  256.     } else break;
  257.     }
  258.     if (nflag && fflag)
  259.     Msg (0, "-f and -n are conflicting options.");
  260.     if ((ShellProg = getenv ("SHELL")) == 0)
  261.     ShellProg = DefaultShell;
  262.     ShellArgs[0] = ShellProg;
  263.     if (ac == 0) {
  264.     ac = 1;
  265.     av = ShellArgs;
  266.     }
  267.     if ((home = getenv ("HOME")) == 0)
  268.     Msg (0, "$HOME is undefined.");
  269.     sprintf (SockPath, "%s/%s", home, SockDir);
  270.     if (stat (SockPath, &st) == -1) {
  271.     if (errno == ENOENT) {
  272.         if (mkdir (SockPath, 0700) == -1)
  273.         Msg (errno, "Cannot make directory %s", SockPath);
  274.         (void) chown (SockPath, getuid (), getgid ());
  275.     } else Msg (errno, "Cannot get status of %s", SockPath);
  276.     } else {
  277.     if ((st.st_mode & S_IFMT) != S_IFDIR)
  278.         Msg (0, "%s is not a directory.", SockPath);
  279.     if ((st.st_mode & 0777) != 0700)
  280.         Msg (0, "Directory %s must have mode 700.", SockPath);
  281.     if (st.st_uid != getuid ())
  282.         Msg (0, "You are not the owner of %s.", SockPath);
  283.     }
  284.     (void) gethostname (HostName, MAXSTR);
  285.     HostName[MAXSTR-1] = '\0';
  286.     if (ap = index (HostName, '.'))
  287.     *ap = '\0';
  288.     strcat (SockPath, "/");
  289.     SockNamePtr = SockPath + strlen (SockPath);
  290.     if ((DevTty = open ("/dev/tty", O_RDWR|O_NDELAY)) == -1)
  291.     Msg (errno, "/dev/tty");
  292.     if (rflag) {
  293.     Attach (MSG_ATTACH);
  294.     Attacher ();
  295.     /*NOTREACHED*/
  296.     }
  297.     if (GetSockName ()) {
  298.     s = MakeClientSocket (1);
  299.     SendCreateMsg (s, ac, av, aflag);
  300.     close (s);
  301.     exit (0);
  302.     }
  303.     switch (fork ()) {
  304.     case -1:
  305.     Msg (errno, "fork");
  306.     /*NOTREACHED*/
  307.     case 0:
  308.     break;
  309.     default:
  310.     Attacher ();
  311.     /*NOTREACHED*/
  312.     }
  313.     AttacherPid = getppid ();
  314.     ServerSocket = s = MakeServerSocket ();
  315.     InitTerm ();
  316.     if (fflag)
  317.     flowctl = 1;
  318.     else if (nflag)
  319.     flowctl = 0;
  320.     MakeNewEnv ();
  321.     GetTTY (0, &OldMode);
  322.     ospeed = (short)OldMode.m_ttyb.sg_ospeed;
  323.     InitUtmp ();
  324. #ifdef LOADAV
  325.     InitKmem ();
  326. #endif
  327.     signal (SIGHUP, SigHup);
  328.     signal (SIGINT, Finit);
  329.     signal (SIGQUIT, Finit);
  330.     signal (SIGTERM, Finit);
  331.     signal (SIGTTIN, SIG_IGN);
  332.     signal (SIGTTOU, SIG_IGN);
  333.     InitKeytab ();
  334.     sprintf (rc, "%.*s/.screenrc", 245, home);
  335.     ReadRc (rc);
  336.     if ((n = MakeWindow (*av, av, aflag, 0, (char *)0)) == -1) {
  337.     SetTTY (0, &OldMode);
  338.     FinitTerm ();
  339.     Kill (AttacherPid, SIGHUP);
  340.     exit (1);
  341.     }
  342.     SetCurrWindow (n);
  343.     HasWindow = 1;
  344.     SetMode (&OldMode, &NewMode);
  345.     SetTTY (0, &NewMode);
  346.     signal (SIGCHLD, SigChld);
  347.     tv.tv_usec = 0;
  348.     while (1) {
  349.     if (status) {
  350.         time (&now);
  351.         if (now - TimeDisplayed < MSGWAIT) {
  352.         tv.tv_sec = MSGWAIT - (now - TimeDisplayed);
  353.         } else RemoveStatus (curr);
  354.     }
  355.     r = 0;
  356.     w = 0;
  357.     if (inlen)
  358.         w |= 1 << curr->ptyfd;
  359.     else
  360.         r |= 1 << 0;
  361.     for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
  362.         if (!(p = *pp))
  363.         continue;
  364.         if ((*pp)->active && status)
  365.         continue;
  366.         if ((*pp)->outlen > 0)
  367.         continue;
  368.         r |= 1 << (*pp)->ptyfd;
  369.     }
  370.     r |= 1 << s;
  371.     (void) fflush (stdout);
  372.     if (GotSignal && !status) {
  373.         SigHandler ();
  374.         continue;
  375.     }
  376.     if (select (32, &r, &w, &x, status ? &tv : (struct timeval *)0) == -1) {
  377.         if (errno == EINTR)
  378.         continue;
  379.         HasWindow = 0;
  380.         Msg (errno, "select");
  381.         /*NOTREACHED*/
  382.     }
  383.     if (GotSignal && !status) {
  384.         SigHandler ();
  385.         continue;
  386.     }
  387.     if (r & 1 << s) {
  388.         RemoveStatus (curr);
  389.         ReceiveMsg (s);
  390.     }
  391.     if (r & 1 << 0) {
  392.         RemoveStatus (curr);
  393.         if (ESCseen) {
  394.         inbuf[0] = Esc;
  395.         inlen = read (0, inbuf+1, IOSIZE-1) + 1;
  396.         ESCseen = 0;
  397.         } else {
  398.         inlen = read (0, inbuf, IOSIZE);
  399.         }
  400.         if (inlen > 0)
  401.         inlen = ProcessInput (inbuf, inlen);
  402.         if (inlen > 0)
  403.         continue;
  404.     }
  405.     if (GotSignal && !status) {
  406.         SigHandler ();
  407.         continue;
  408.     }
  409.     if (w & 1 << curr->ptyfd && inlen > 0) {
  410.         if ((len = write (curr->ptyfd, inbuf, inlen)) > 0) {
  411.         inlen -= len;
  412.         bcopy (inbuf+len, inbuf, inlen);
  413.         }
  414.     }
  415.     if (GotSignal && !status) {
  416.         SigHandler ();
  417.         continue;
  418.     }
  419.     for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
  420.         if (!(p = *pp))
  421.         continue;
  422.         if (p->outlen) {
  423.         WriteString (p, p->outbuf, p->outlen);
  424.         } else if (r & 1 << p->ptyfd) {
  425.         if ((len = read (p->ptyfd, buf, IOSIZE)) == -1) {
  426.             if (errno == EWOULDBLOCK)
  427.             len = 0;
  428.         }
  429.         if (len > 0)
  430.             WriteString (p, buf, len);
  431.         }
  432.         if (p->bell) {
  433.         p->bell = 0;
  434.         Msg (0, MakeBellMsg (pp-wtab));
  435.         }
  436.     }
  437.     if (GotSignal && !status)
  438.         SigHandler ();
  439.     }
  440.     /*NOTREACHED*/
  441. }
  442.  
  443. static SigHandler () {
  444.     while (GotSignal) {
  445.     GotSignal = 0;
  446.     DoWait ();
  447.     }
  448. }
  449.  
  450. static SigChld () {
  451.     GotSignal = 1;
  452. }
  453.  
  454. static SigHup () {
  455.     Detach (0);
  456. }
  457.  
  458. static DoWait () {
  459.     register pid;
  460.     register struct win **pp;
  461.     union wait wstat;
  462.  
  463.     while ((pid = wait3 (&wstat, WNOHANG|WUNTRACED, NULL)) > 0) {
  464.     for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
  465.         if (*pp && pid == (*pp)->wpid) {
  466.         if (WIFSTOPPED (wstat)) {
  467.             (void) killpg (getpgrp ((*pp)->wpid), SIGCONT);
  468.         } else {
  469.             KillWindow (pp);
  470.         }
  471.         }
  472.     }
  473.     }
  474.     CheckWindows ();
  475. }
  476.  
  477. static KillWindow (pp) struct win **pp; {
  478.     if (*pp == curr)
  479.     curr = 0;
  480.     if (*pp == other)
  481.     other = 0;
  482.     FreeWindow (*pp);
  483.     *pp = 0;
  484. }
  485.  
  486. static CheckWindows () {
  487.     register struct win **pp;
  488.  
  489.     /* If the current window disappeared and the "other" window is still
  490.      * there, switch to the "other" window, else switch to the window
  491.      * with the lowest index.
  492.      * If there current window is still there, but the "other" window
  493.      * vanished, "SetCurrWindow" is called in order to assign a new value
  494.      * to "other".
  495.      * If no window is alive at all, exit.
  496.      */
  497.     if (!curr && other) {
  498.     SwitchWindow (OtherNum);
  499.     return;
  500.     }
  501.     if (curr && !other) {
  502.     SetCurrWindow (CurrNum);
  503.     return;
  504.     }
  505.     for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
  506.     if (*pp) {
  507.         if (!curr)
  508.         SwitchWindow (pp-wtab);
  509.         return;
  510.     }
  511.     }
  512.     Finit ();
  513. }
  514.  
  515. static Finit () {
  516.     register struct win *p, **pp;
  517.  
  518.     for (pp = wtab; pp < wtab+MAXWIN; ++pp) {
  519.     if (p = *pp)
  520.         FreeWindow (p);
  521.     }
  522.     SetTTY (0, &OldMode);
  523.     FinitTerm ();
  524.     printf ("[screen is terminating]\n");
  525.     Kill (AttacherPid, SIGHUP);
  526.     exit (0);
  527. }
  528.  
  529. static InitKeytab () {
  530.     register i;
  531.  
  532.     ktab['h'].type = ktab[Ctrl('h')].type = KEY_HARDCOPY;
  533.     ktab['z'].type = ktab[Ctrl('z')].type = KEY_SUSPEND;
  534.     ktab['c'].type = ktab[Ctrl('c')].type = KEY_SHELL;
  535.     ktab[' '].type = ktab[Ctrl(' ')].type = 
  536.     ktab['n'].type = ktab[Ctrl('n')].type = KEY_NEXT;
  537.     ktab['-'].type = ktab['p'].type = ktab[Ctrl('p')].type = KEY_PREV;
  538.     ktab['k'].type = ktab[Ctrl('k')].type = KEY_KILL;
  539.     ktab['l'].type = ktab[Ctrl('l')].type = KEY_REDISPLAY;
  540.     ktab['w'].type = ktab[Ctrl('w')].type = KEY_WINDOWS;
  541.     ktab['v'].type = ktab[Ctrl('v')].type = KEY_VERSION;
  542.     ktab['q'].type = ktab[Ctrl('q')].type = KEY_XON;
  543.     ktab['s'].type = ktab[Ctrl('s')].type = KEY_XOFF;
  544.     ktab['t'].type = ktab[Ctrl('t')].type = KEY_INFO;
  545.     ktab['.'].type = KEY_TERMCAP;
  546.     ktab[Ctrl('\\')].type = KEY_QUIT;
  547.     ktab['d'].type = ktab[Ctrl('d')].type = KEY_DETACH;
  548.     ktab[Esc].type = KEY_OTHER;
  549.     for (i = 0; i <= 9; i++)
  550.     ktab[i+'0'].type = KEY_0+i;
  551. }
  552.  
  553. static ProcessInput (buf, len) char *buf; {
  554.     register n, k;
  555.     register char *s, *p;
  556.     register struct win **pp;
  557.  
  558.     for (s = p = buf; len > 0; len--, s++) {
  559.     if (*s == Esc) {
  560.         if (len > 1) {
  561.         len--; s++;
  562.         k = ktab[*s].type;
  563.         if (*s == MetaEsc) {
  564.             *p++ = Esc;
  565.         } else if (k >= KEY_0 && k <= KEY_9) {
  566.             p = buf;
  567.             SwitchWindow (k - KEY_0);
  568.         } else switch (ktab[*s].type) {
  569.         case KEY_TERMCAP:
  570.             p = buf;
  571.             WriteFile (0);
  572.             break;
  573.         case KEY_HARDCOPY:
  574.             p = buf;
  575.             WriteFile (1);
  576.             break;
  577.         case KEY_SUSPEND:
  578.             p = buf;
  579.             Detach (1);
  580.             break;
  581.         case KEY_SHELL:
  582.             p = buf;
  583.             if ((n = MakeWindow (ShellProg, ShellArgs,
  584.                 0, 0, (char *)0)) != -1)
  585.             SwitchWindow (n);
  586.             break;
  587.         case KEY_NEXT:
  588.             p = buf;
  589.             if (MoreWindows ())
  590.             SwitchWindow (NextWindow ());
  591.             break;
  592.         case KEY_PREV:
  593.             p = buf;
  594.             if (MoreWindows ())
  595.             SwitchWindow (PreviousWindow ());
  596.             break;
  597.         case KEY_KILL:
  598.             p = buf;
  599.             FreeWindow (wtab[CurrNum]);
  600.             if (other == curr)
  601.             other = 0;
  602.             curr = wtab[CurrNum] = 0;
  603.             CheckWindows ();
  604.             break;
  605.         case KEY_QUIT:
  606.             for (pp = wtab; pp < wtab+MAXWIN; ++pp)
  607.             if (*pp) FreeWindow (*pp);
  608.             Finit ();
  609.             /*NOTREACHED*/
  610.         case KEY_DETACH:
  611.             p = buf;
  612.             Detach (0);
  613.             break;
  614.         case KEY_REDISPLAY:
  615.             p = buf;
  616.             Activate (wtab[CurrNum]);
  617.             break;
  618.         case KEY_WINDOWS:
  619.             p = buf;
  620.             ShowWindows ();
  621.             break;
  622.         case KEY_VERSION:
  623.             p = buf;
  624.             Msg (0, "%s  %s", ScreenVersion, AnsiVersion);
  625.             break;
  626.         case KEY_INFO:
  627.             p = buf;
  628.             ShowInfo ();
  629.             break;
  630.         case KEY_OTHER:
  631.             p = buf;
  632.             if (MoreWindows ())
  633.             SwitchWindow (OtherNum);
  634.             break;
  635.         case KEY_XON:
  636.             *p++ = Ctrl('q');
  637.             break;
  638.         case KEY_XOFF:
  639.             *p++ = Ctrl('s');
  640.             break;
  641.         case KEY_CREATE:
  642.             p = buf;
  643.             if ((n = MakeWindow (ktab[*s].args[0], ktab[*s].args,
  644.                 0, 0, (char *)0)) != -1)
  645.             SwitchWindow (n);
  646.             break;
  647.         }
  648.         } else ESCseen = 1;
  649.     } else *p++ = *s;
  650.     }
  651.     return p - buf;
  652. }
  653.  
  654. static SwitchWindow (n) {
  655.     if (!wtab[n])
  656.     return;
  657.     SetCurrWindow (n);
  658.     Activate (wtab[n]);
  659. }
  660.  
  661. static SetCurrWindow (n) {
  662.     /*
  663.      * If we come from another window, this window becomes the
  664.      * "other" window:
  665.      */
  666.     if (curr) {
  667.     curr->active = 0;
  668.     other = curr;
  669.     OtherNum = CurrNum;
  670.     }
  671.     CurrNum = n;
  672.     curr = wtab[n];
  673.     curr->active = 1;
  674.     /*
  675.      * If the "other" window is currently undefined (at program start
  676.      * or because it has died), or if the "other" window is equal to the
  677.      * one just selected, we try to find a new one:
  678.      */
  679.     if (other == 0 || other == curr) {
  680.     OtherNum = NextWindow ();
  681.     other = wtab[OtherNum];
  682.     }
  683. }
  684.  
  685. static NextWindow () {
  686.     register struct win **pp;
  687.  
  688.     for (pp = wtab+CurrNum+1; pp != wtab+CurrNum; ++pp) {
  689.     if (pp == wtab+MAXWIN)
  690.         pp = wtab;
  691.     if (*pp)
  692.         break;
  693.     }
  694.     return pp-wtab;
  695. }
  696.  
  697. static PreviousWindow () {
  698.     register struct win **pp;
  699.  
  700.     for (pp = wtab+CurrNum-1; pp != wtab+CurrNum; --pp) {
  701.     if (pp < wtab)
  702.         pp = wtab+MAXWIN-1;
  703.     if (*pp)
  704.         break;
  705.     }
  706.     return pp-wtab;
  707. }
  708.  
  709. static MoreWindows () {
  710.     register struct win **pp;
  711.     register n;
  712.  
  713.     for (n = 0, pp = wtab; pp < wtab+MAXWIN; ++pp)
  714.     if (*pp) ++n;
  715.     if (n <= 1)
  716.     Msg (0, "No other window.");
  717.     return n > 1;
  718. }
  719.  
  720. static FreeWindow (wp) struct win *wp; {
  721.     register i;
  722.  
  723.     RemoveUtmp (wp->slot);
  724.     (void) chmod (wp->tty, 0666);
  725.     (void) chown (wp->tty, 0, 0);
  726.     close (wp->ptyfd);
  727.     for (i = 0; i < rows; ++i) {
  728.     free (wp->image[i]);
  729.     free (wp->attr[i]);
  730.     free (wp->font[i]);
  731.     }
  732.     free (wp->image);
  733.     free (wp->attr);
  734.     free (wp->font);
  735.     free (wp);
  736. }
  737.  
  738. static MakeWindow (prog, args, aflag, StartAt, dir)
  739.     char *prog, **args, *dir; {
  740.     register struct win **pp, *p;
  741.     register char **cp;
  742.     register n, f;
  743.     int tf;
  744.     int mypid;
  745.     char ebuf[10];
  746.  
  747.     pp = wtab+StartAt;
  748.     do {
  749.     if (*pp == 0)
  750.         break;
  751.     if (++pp == wtab+MAXWIN)
  752.         pp = wtab;
  753.     } while (pp != wtab+StartAt);
  754.     if (*pp) {
  755.     Msg (0, "No more windows.");
  756.     return -1;
  757.     }
  758.     n = pp - wtab;
  759.     if ((f = OpenPTY ()) == -1) {
  760.     Msg (0, "No more PTYs.");
  761.     return -1;
  762.     }
  763.     (void) fcntl (f, F_SETFL, FNDELAY);
  764.     if ((p = *pp = (struct win *)malloc (sizeof (struct win))) == 0) {
  765. nomem:
  766.     Msg (0, "Out of memory.");
  767.     return -1;
  768.     }
  769.     if ((p->image = (char **)malloc (rows * sizeof (char *))) == 0)
  770.     goto nomem;
  771.     for (cp = p->image; cp < p->image+rows; ++cp) {
  772.     if ((*cp = malloc (cols)) == 0)
  773.         goto nomem;
  774.     bclear (*cp, cols);
  775.     }
  776.     if ((p->attr = (char **)malloc (rows * sizeof (char *))) == 0)
  777.     goto nomem;
  778.     for (cp = p->attr; cp < p->attr+rows; ++cp) {
  779.     if ((*cp = malloc (cols)) == 0)
  780.         goto nomem;
  781.     bzero (*cp, cols);
  782.     }
  783.     if ((p->font = (char **)malloc (rows * sizeof (char *))) == 0)
  784.     goto nomem;
  785.     for (cp = p->font; cp < p->font+rows; ++cp) {
  786.     if ((*cp = malloc (cols)) == 0)
  787.         goto nomem;
  788.     bzero (*cp, cols);
  789.     }
  790.     if ((p->tabs = malloc (cols+1)) == 0)  /* +1 because 0 <= x <= cols */
  791.     goto nomem;
  792.     ResetScreen (p);
  793.     p->aflag = aflag;
  794.     p->active = 0;
  795.     p->bell = 0;
  796.     p->outlen = 0;
  797.     p->ptyfd = f;
  798.     strncpy (p->cmd, Filename (args[0]), MAXSTR-1);
  799.     p->cmd[MAXSTR-1] = '\0';
  800.     strncpy (p->tty, TtyName, MAXSTR-1);
  801.     (void) chown (TtyName, getuid (), getgid ());
  802.     (void) chmod (TtyName, TtyMode);
  803.     p->slot = SetUtmp (TtyName);
  804.     switch (p->wpid = fork ()) {
  805.     case -1:
  806.     Msg (errno, "fork");
  807.     free ((char *)p);
  808.     return -1;
  809.     case 0:
  810.     signal (SIGHUP, SIG_DFL);
  811.     signal (SIGINT, SIG_DFL);
  812.     signal (SIGQUIT, SIG_DFL);
  813.     signal (SIGTERM, SIG_DFL);
  814.     signal (SIGTTIN, SIG_DFL);
  815.     signal (SIGTTOU, SIG_DFL);
  816.     setuid (getuid ());
  817.     setgid (getgid ());
  818.     if (dir && chdir (dir) == -1) {
  819.         SendErrorMsg ("Cannot chdir to %s: %s", dir, sys_errlist[errno]);
  820.         exit (1);
  821.     }
  822.     mypid = getpid ();
  823.     ioctl (DevTty, TIOCNOTTY, (char *)0);
  824.     if ((tf = open (TtyName, O_RDWR)) == -1) {
  825.         SendErrorMsg ("Cannot open %s: %s", TtyName, sys_errlist[errno]);
  826.         exit (1);
  827.     }
  828.     (void) dup2 (tf, 0);
  829.     (void) dup2 (tf, 1);
  830.     (void) dup2 (tf, 2);
  831.     for (f = getdtablesize () - 1; f > 2; f--)
  832.         close (f);
  833.     ioctl (0, TIOCSPGRP, &mypid);
  834.     (void) setpgrp (0, mypid);
  835.     SetTTY (0, &OldMode);
  836.     NewEnv[2] = MakeTermcap (aflag);
  837.     sprintf (ebuf, "WINDOW=%d", n);
  838.     NewEnv[3] = ebuf;
  839.     execvpe (prog, args, NewEnv);
  840.     SendErrorMsg ("Cannot exec %s: %s", prog, sys_errlist[errno]);
  841.     exit (1);
  842.     }
  843.     return n;
  844. }
  845.  
  846. static execvpe (prog, args, env) char *prog, **args, **env; {
  847.     register char *path, *p;
  848.     char buf[1024];
  849.     char *shargs[MAXARGS+1];
  850.     register i, eaccess = 0;
  851.  
  852.     if (prog[0] == '/')
  853.     path = "";
  854.     else if ((path = getenv ("PATH")) == 0)
  855.     path = DefaultPath;
  856.     do {
  857.     p = buf;
  858.     while (*path && *path != ':')
  859.         *p++ = *path++;
  860.     if (p > buf)
  861.         *p++ = '/';
  862.     strcpy (p, prog);
  863.     if (*path)
  864.         ++path;
  865.     execve (buf, args, env);
  866.     switch (errno) {
  867.     case ENOEXEC:
  868.         shargs[0] = DefaultShell;
  869.         shargs[1] = buf;
  870.         for (i = 1; shargs[i+1] = args[i]; ++i)
  871.         ;
  872.         execve (DefaultShell, shargs, env);
  873.         return;
  874.     case EACCES:
  875.         eaccess = 1;
  876.         break;
  877.     case ENOMEM: case E2BIG: case ETXTBSY:
  878.         return;
  879.     }
  880.     } while (*path);
  881.     if (eaccess)
  882.     errno = EACCES;
  883. }
  884.  
  885. static WriteFile (dump) {   /* dump==0: create .termcap, dump==1: hardcopy */
  886.     register i, j, k;
  887.     register char *p;
  888.     register FILE *f;
  889.     char fn[1024];
  890.     int pid, s;
  891.  
  892.     if (dump)
  893.     sprintf (fn, "hardcopy.%d", CurrNum);
  894.     else
  895.     sprintf (fn, "%s/%s/.termcap", home, SockDir);
  896.     switch (pid = fork ()) {
  897.     case -1:
  898.     Msg (errno, "fork");
  899.     return;
  900.     case 0:
  901.     setuid (getuid ());
  902.     setgid (getgid ());
  903.     if ((f = fopen (fn, "w")) == NULL)
  904.         exit (1);
  905.     if (dump) {
  906.         for (i = 0; i < rows; ++i) {
  907.         p = curr->image[i];
  908.         for (k = cols-1; k >= 0 && p[k] == ' '; --k) ;
  909.         for (j = 0; j <= k; ++j)
  910.             putc (p[j], f);
  911.         putc ('\n', f);
  912.         }
  913.     } else {
  914.         if (p = index (MakeTermcap (curr->aflag), '=')) {
  915.         fputs (++p, f);
  916.         putc ('\n', f);
  917.         }
  918.     }
  919.     (void) fclose (f);
  920.     exit (0);
  921.     default:
  922.     while ((i = wait (&s)) != pid)
  923.         if (i == -1) return;
  924.     if ((s >> 8) & 0377)
  925.         Msg (0, "Cannot open \"%s\".", fn);
  926.     else
  927.         Msg (0, "%s written to \"%s\".", dump ? "Screen image" :
  928.         "Termcap entry", fn);
  929.     }
  930. }
  931.  
  932. static ShowWindows () {
  933.     char buf[1024];
  934.     register char *s;
  935.     register struct win **pp, *p;
  936.  
  937.     for (s = buf, pp = wtab; pp < wtab+MAXWIN; ++pp) {
  938.     if ((p = *pp) == 0)
  939.         continue;
  940.     if (s - buf + 5 + strlen (p->cmd) > cols-1)
  941.         break;
  942.     if (s > buf) {
  943.         *s++ = ' '; *s++ = ' ';
  944.     }
  945.     *s++ = pp - wtab + '0';
  946.     if (p == curr)
  947.         *s++ = '*';
  948.     else if (p == other)
  949.         *s++ = '-';
  950.     *s++ = ' ';
  951.     strcpy (s, p->cmd);
  952.     s += strlen (s);
  953.     }
  954.     Msg (0, buf);
  955. }
  956.  
  957. static ShowInfo () {
  958.     char buf[1024], *p;
  959.     register struct win *wp = curr;
  960.     register i;
  961.     struct tm *tp;
  962.     time_t now;
  963.  
  964.     time (&now);
  965.     tp = localtime (&now);
  966.     sprintf (buf, "%2d:%02.2d:%02.2d %s", tp->tm_hour, tp->tm_min, tp->tm_sec,
  967.     HostName);
  968. #ifdef LOADAV
  969.     if (avenrun && GetAvenrun ()) {
  970.     p = buf + strlen (buf);
  971. #ifdef SUNLOADAV
  972.     sprintf (p, " %2.2f %2.2f %2.2f", (double)loadav[0]/FSCALE,
  973.         (double)loadav[1]/FSCALE, (double)loadav[2]/FSCALE);
  974. #else
  975. #ifdef alliant
  976.     sprintf (p, " %2.2f %2.2f %2.2f %2.2f", (double)loadav[0]/100,
  977.         (double)loadav[1]/100, (double)loadav[2]/100,
  978.         (double)loadav[3]/100);
  979. #else
  980.     sprintf (p, " %2.2f %2.2f %2.2f", loadav[0], loadav[1], loadav[2]);
  981. #endif
  982. #endif
  983.     }
  984. #endif
  985.     p = buf + strlen (buf);
  986.     sprintf (p, " (%d,%d) %cflow %cins %corg %cwrap %cpad", wp->y, wp->x,
  987.     flowctl ? '+' : '-',
  988.     wp->insert ? '+' : '-', wp->origin ? '+' : '-',
  989.     wp->wrap ? '+' : '-', wp->keypad ? '+' : '-');
  990.     if (ISO2022) {
  991.     p = buf + strlen (buf);
  992.     sprintf (p, " G%1d [", wp->LocalCharset);
  993.     for (i = 0; i < 4; i++)
  994.         p[i+5] = wp->charsets[i] ? wp->charsets[i] : 'B';
  995.     p[9] = ']';
  996.     p[10] = '\0';
  997.     }
  998.     Msg (0, buf);
  999. }
  1000.  
  1001. static OpenPTY () {
  1002.     register char *p, *l, *d;
  1003.     register i, f, tf;
  1004.  
  1005.     strcpy (PtyName, PtyProto);
  1006.     strcpy (TtyName, TtyProto);
  1007.     for (p = PtyName, i = 0; *p != 'X'; ++p, ++i) ;
  1008.     for (l = "qpr"; *p = *l; ++l) {
  1009.     for (d = "0123456789abcdef"; p[1] = *d; ++d) {
  1010.         if ((f = open (PtyName, O_RDWR)) != -1) {
  1011.         TtyName[i] = p[0];
  1012.         TtyName[i+1] = p[1];
  1013.         if ((tf = open (TtyName, O_RDWR)) != -1) {
  1014.             close (tf);
  1015.             return f;
  1016.         }
  1017.         close (f);
  1018.         }
  1019.     }
  1020.     }
  1021.     return -1;
  1022. }
  1023.  
  1024. static SetTTY (fd, mp) struct mode *mp; {
  1025.     ioctl (fd, TIOCSETP, &mp->m_ttyb);
  1026.     ioctl (fd, TIOCSETC, &mp->m_tchars);
  1027.     ioctl (fd, TIOCSLTC, &mp->m_ltchars);
  1028.     ioctl (fd, TIOCLSET, &mp->m_lmode);
  1029.     ioctl (fd, TIOCSETD, &mp->m_ldisc);
  1030. }
  1031.  
  1032. static GetTTY (fd, mp) struct mode *mp; {
  1033.     ioctl (fd, TIOCGETP, &mp->m_ttyb);
  1034.     ioctl (fd, TIOCGETC, &mp->m_tchars);
  1035.     ioctl (fd, TIOCGLTC, &mp->m_ltchars);
  1036.     ioctl (fd, TIOCLGET, &mp->m_lmode);
  1037.     ioctl (fd, TIOCGETD, &mp->m_ldisc);
  1038. }
  1039.  
  1040. static SetMode (op, np) struct mode *op, *np; {
  1041.     *np = *op;
  1042.     np->m_ttyb.sg_flags &= ~(CRMOD|ECHO);
  1043.     np->m_ttyb.sg_flags |= CBREAK;
  1044.     np->m_tchars.t_intrc = -1;
  1045.     np->m_tchars.t_quitc = -1;
  1046.     if (!flowctl) {
  1047.     np->m_tchars.t_startc = -1;
  1048.     np->m_tchars.t_stopc = -1;
  1049.     }
  1050.     np->m_ltchars.t_suspc = -1;
  1051.     np->m_ltchars.t_dsuspc = -1;
  1052.     np->m_ltchars.t_flushc = -1;
  1053.     np->m_ltchars.t_lnextc = -1;
  1054. }
  1055.  
  1056. static char *GetTtyName () {
  1057.     register char *p;
  1058.     register n;
  1059.  
  1060.     for (p = 0, n = 0; n <= 2 && !(p = ttyname (n)); n++)
  1061.     ;
  1062.     if (!p || *p == '\0')
  1063.     Msg (0, "screen must run on a tty.");
  1064.     return p;
  1065. }
  1066.  
  1067. static Attach (how) {
  1068.     register s, lasts, found = 0;
  1069.     register DIR *dirp;
  1070.     register struct direct *dp;
  1071.     struct msg m;
  1072.     char last[MAXNAMLEN+1];
  1073.  
  1074.     if (SockName) {
  1075.     if ((lasts = MakeClientSocket (0)) == -1)
  1076.         if (how == MSG_CONT)
  1077.         Msg (0,
  1078.             "This screen has already been continued from elsewhere.");
  1079.         else
  1080.         Msg (0, "There is no screen to be resumed from %s.", SockName);
  1081.     } else {
  1082.     if ((dirp = opendir (SockPath)) == NULL)
  1083.         Msg (0, "Cannot open %s", SockPath);
  1084.     while ((dp = readdir (dirp)) != NULL) {
  1085.         SockName = dp->d_name;
  1086.         if (SockName[0] == '.')
  1087.         continue;
  1088.         if ((s = MakeClientSocket (0)) != -1) {
  1089.         if (found == 0) {
  1090.             strcpy (last, SockName);
  1091.             lasts = s;
  1092.         } else {
  1093.             if (found == 1) {
  1094.             printf ("There are detached screens on:\n");
  1095.             printf ("   %s\n", last);
  1096.             close (lasts);
  1097.             }
  1098.             printf ("   %s\n", SockName);
  1099.             close (s);
  1100.         }
  1101.         found++;
  1102.         }
  1103.     }
  1104.     if (found == 0)
  1105.         Msg (0, "There is no screen to be resumed.");
  1106.     if (found > 1)
  1107.         Msg (0, "Type \"screen -r host.tty\" to resume one of them.");
  1108.     closedir (dirp);
  1109.     strcpy (SockNamePtr, last);
  1110.     SockName = SockNamePtr;
  1111.     }
  1112.     m.type = how;
  1113.     strcpy (m.m.attach.tty, GetTtyName ());
  1114.     m.m.attach.apid = getpid ();
  1115.     if (write (lasts, (char *)&m, sizeof (m)) != sizeof (m))
  1116.     Msg (errno, "write");
  1117. }
  1118.  
  1119. static AttacherFinit () {
  1120.     exit (0);
  1121. }
  1122.  
  1123. static ReAttach () {
  1124.     Attach (MSG_CONT);
  1125. }
  1126.  
  1127. static Attacher () {
  1128.     signal (SIGHUP, AttacherFinit);
  1129.     signal (SIGCONT, ReAttach);
  1130.     while (1)
  1131.     pause ();
  1132. }
  1133.  
  1134. static Detach (suspend) {
  1135.     register struct win **pp;
  1136.  
  1137.     if (Detached)
  1138.     return;
  1139.     signal (SIGHUP, SIG_IGN);
  1140.     SetTTY (0, &OldMode);
  1141.     FinitTerm ();
  1142.     if (suspend) {
  1143.     Kill (AttacherPid, SIGTSTP);
  1144.     } else {
  1145.     for (pp = wtab; pp < wtab+MAXWIN; ++pp)
  1146.         if (*pp) RemoveUtmp ((*pp)->slot);
  1147.     printf ("\n[detached]\n");
  1148.     Kill (AttacherPid, SIGHUP);
  1149.     AttacherPid = 0;
  1150.     }
  1151.     close (0);
  1152.     close (1);
  1153.     close (2);
  1154.     ioctl (DevTty, TIOCNOTTY, (char *)0);
  1155.     Detached = 1;
  1156.     do {
  1157.     ReceiveMsg (ServerSocket); 
  1158.     } while (Detached);
  1159.     if (!suspend)
  1160.     for (pp = wtab; pp < wtab+MAXWIN; ++pp)
  1161.         if (*pp) (*pp)->slot = SetUtmp ((*pp)->tty);
  1162.     signal (SIGHUP, SigHup);
  1163. }
  1164.  
  1165. static Kill (pid, sig) {
  1166.     if (pid != 0)
  1167.     (void) kill (pid, sig);
  1168. }
  1169.  
  1170. static GetSockName () {
  1171.     register client;
  1172.     static char buf[2*MAXSTR];
  1173.  
  1174.     if (!mflag && (SockName = getenv ("STY")) != 0 && *SockName != '\0') {
  1175.     client = 1;
  1176.     setuid (getuid ());
  1177.     setgid (getgid ());
  1178.     } else {
  1179.     sprintf (buf, "%s.%s", HostName, Filename (GetTtyName ()));
  1180.     SockName = buf;
  1181.     client = 0;
  1182.     }
  1183.     return client;
  1184. }
  1185.  
  1186. static MakeServerSocket () {
  1187.     register s;
  1188.     struct sockaddr_un a;
  1189.     char *p;
  1190.  
  1191.     if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
  1192.     Msg (errno, "socket");
  1193.     a.sun_family = AF_UNIX;
  1194.     strcpy (SockNamePtr, SockName);
  1195.     strcpy (a.sun_path, SockPath);
  1196.     if (connect (s, (struct sockaddr *)&a, strlen (SockPath)+2) != -1) {
  1197.     p = Filename (SockPath);
  1198.     Msg (0, "You have already a screen running on %s.\n\
  1199. If it has been detached, try \"screen -r\".", p);
  1200.     /*NOTREACHED*/
  1201.     }
  1202.     (void) unlink (SockPath);
  1203.     if (bind (s, (struct sockaddr *)&a, strlen (SockPath)+2) == -1)
  1204.     Msg (errno, "bind");
  1205.     (void) chown (SockPath, getuid (), getgid ());
  1206.     if (listen (s, 5) == -1)
  1207.     Msg (errno, "listen");
  1208.     return s;
  1209. }
  1210.  
  1211. static MakeClientSocket (err) {
  1212.     register s;
  1213.     struct sockaddr_un a;
  1214.  
  1215.     if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
  1216.     Msg (errno, "socket");
  1217.     a.sun_family = AF_UNIX;
  1218.     strcpy (SockNamePtr, SockName);
  1219.     strcpy (a.sun_path, SockPath);
  1220.     if (connect (s, (struct sockaddr *)&a, strlen (SockPath)+2) == -1) {
  1221.     if (err) {
  1222.         Msg (errno, "connect: %s", SockPath);
  1223.     } else {
  1224.         close (s);
  1225.         return -1;
  1226.     }
  1227.     }
  1228.     return s;
  1229. }
  1230.  
  1231. static SendCreateMsg (s, ac, av, aflag) char **av; {
  1232.     struct msg m;
  1233.     register char *p;
  1234.     register len, n;
  1235.  
  1236.     m.type = MSG_CREATE;
  1237.     p = m.m.create.line;
  1238.     for (n = 0; ac > 0 && n < MAXARGS-1; ++av, --ac, ++n) {
  1239.     len = strlen (*av) + 1;
  1240.     if (p + len >= m.m.create.line+MAXLINE)
  1241.         break;
  1242.     strcpy (p, *av);
  1243.     p += len;
  1244.     }
  1245.     m.m.create.nargs = n;
  1246.     m.m.create.aflag = aflag;
  1247.     if (getwd (m.m.create.dir) == 0)
  1248.     Msg (0, "%s", m.m.create.dir);
  1249.     if (write (s, (char *)&m, sizeof (m)) != sizeof (m))
  1250.     Msg (errno, "write");
  1251. }
  1252.  
  1253. /*VARARGS1*/
  1254. static SendErrorMsg (fmt, p1, p2, p3, p4, p5, p6) char *fmt; {
  1255.     register s;
  1256.     struct msg m;
  1257.  
  1258.     s = MakeClientSocket (1);
  1259.     m.type = MSG_ERROR;
  1260.     sprintf (m.m.message, fmt, p1, p2, p3, p4, p5, p6);
  1261.     (void) write (s, (char *)&m, sizeof (m));
  1262.     close (s);
  1263.     sleep (2);
  1264. }
  1265.  
  1266. static ReceiveMsg (s) {
  1267.     register ns;
  1268.     struct sockaddr_un a;
  1269.     int left, len = sizeof (a);
  1270.     struct msg m;
  1271.     char *p;
  1272.  
  1273.     if ((ns = accept (s, (struct sockaddr *)&a, &len)) == -1) {
  1274.     Msg (errno, "accept");
  1275.     return;
  1276.     }
  1277.     p = (char *)&m;
  1278.     left = sizeof (m);
  1279.     while (left > 0 && (len = read (ns, p, left)) > 0) {
  1280.     p += len;
  1281.     left -= len;
  1282.     }
  1283.     close (ns);
  1284.     if (len == -1)
  1285.     Msg (errno, "read");
  1286.     if (left > 0)
  1287.     return;
  1288.     switch (m.type) {
  1289.     case MSG_CREATE:
  1290.     if (!Detached)
  1291.         ExecCreate (&m);
  1292.     break;
  1293.     case MSG_CONT:
  1294.     if (m.m.attach.apid != AttacherPid || !Detached)
  1295.         break;    /* Intruder Alert */
  1296.     /*FALLTHROUGH*/
  1297.     case MSG_ATTACH:
  1298.     if (Detached) {
  1299.         if (kill (m.m.attach.apid, 0) == 0 &&
  1300.             open (m.m.attach.tty, O_RDWR) == 0) {
  1301.         (void) dup (0);
  1302.         (void) dup (0);
  1303.         AttacherPid = m.m.attach.apid;
  1304.         Detached = 0;
  1305.         GetTTY (0, &OldMode);
  1306.         SetMode (&OldMode, &NewMode);
  1307.         SetTTY (0, &NewMode);
  1308.         Activate (wtab[CurrNum]);
  1309.         }
  1310.     } else {
  1311.         Kill (m.m.attach.apid, SIGHUP);
  1312.         Msg (0, "Not detached.");
  1313.     }
  1314.     break;
  1315.     case MSG_ERROR:
  1316.     Msg (0, "%s", m.m.message);
  1317.     break;
  1318.     default:
  1319.     Msg (0, "Invalid message (type %d).", m.type);
  1320.     }
  1321. }
  1322.  
  1323. static ExecCreate (mp) struct msg *mp; {
  1324.     char *args[MAXARGS];
  1325.     register n;
  1326.     register char **pp = args, *p = mp->m.create.line;
  1327.  
  1328.     for (n = mp->m.create.nargs; n > 0; --n) {
  1329.     *pp++ = p;
  1330.     p += strlen (p) + 1;
  1331.     }
  1332.     *pp = 0;
  1333.     if ((n = MakeWindow (mp->m.create.line, args, mp->m.create.aflag, 0,
  1334.         mp->m.create.dir)) != -1)
  1335.     SwitchWindow (n);
  1336. }
  1337.  
  1338. static ReadRc (fn) char *fn; {
  1339.     FILE *f;
  1340.     register char *p, **pp, **ap;
  1341.     register argc, num, c;
  1342.     char buf[256];
  1343.     char *args[MAXARGS];
  1344.     int key;
  1345.  
  1346.     ap = args;
  1347.     if (access (fn, R_OK) == -1)
  1348.     return;
  1349.     if ((f = fopen (fn, "r")) == NULL)
  1350.     return;
  1351.     while (fgets (buf, 256, f) != NULL) {
  1352.     if (p = rindex (buf, '\n'))
  1353.         *p = '\0';
  1354.     if ((argc = Parse (fn, buf, ap)) == 0)
  1355.         continue;
  1356.     if (strcmp (ap[0], "escape") == 0) {
  1357.         p = ap[1];
  1358.         if (argc < 2 || strlen (p) != 2)
  1359.         Msg (0, "%s: two characters required after escape.", fn);
  1360.         Esc = *p++;
  1361.         MetaEsc = *p;
  1362.     } else if (strcmp (ap[0], "chdir") == 0) {
  1363.         p = argc < 2 ? home : ap[1];
  1364.         if (chdir (p) == -1)
  1365.         Msg (errno, "%s", p);
  1366.     } else if (strcmp (ap[0], "mode") == 0) {
  1367.         if (argc != 2) {
  1368.         Msg (0, "%s: mode: one argument required.", fn);
  1369.         } else if (!IsNum (ap[1], 7)) {
  1370.         Msg (0, "%s: mode: octal number expected.", fn);
  1371.         } else (void) sscanf (ap[1], "%o", &TtyMode);
  1372.     } else if (strcmp (ap[0], "bell") == 0) {
  1373.         if (argc != 2) {
  1374.         Msg (0, "%s: bell: one argument required.", fn);
  1375.         } else {
  1376.         if ((BellString = malloc (strlen (ap[1]) + 1)) == 0)
  1377.             Msg (0, "Out of memory.");
  1378.         strcpy (BellString, ap[1]);
  1379.         }
  1380.     } else if (strcmp (ap[0], "screen") == 0) {
  1381.         num = 0;
  1382.         if (argc > 1 && IsNum (ap[1], 10)) {
  1383.         num = atoi (ap[1]);
  1384.         if (num < 0 || num > MAXWIN-1)
  1385.             Msg (0, "%s: illegal screen number %d.", fn, num);
  1386.         --argc; ++ap;
  1387.         }
  1388.         if (argc < 2) {
  1389.         ap[1] = ShellProg; argc = 2;
  1390.         }
  1391.         ap[argc] = 0;
  1392.         (void) MakeWindow (ap[1], ap+1, 0, num, (char *)0);
  1393.     } else if (strcmp (ap[0], "bind") == 0) {
  1394.         p = ap[1];
  1395.         if (argc < 2 || *p == '\0')
  1396.         Msg (0, "%s: key expected after bind.", fn);
  1397.         if (p[1] == '\0') {
  1398.         key = *p;
  1399.         } else if (p[0] == '^' && p[1] != '\0' && p[2] == '\0') {
  1400.         c = p[1];
  1401.         if (isupper (c))
  1402.             p[1] = tolower (c);    
  1403.         key = Ctrl(c);
  1404.         } else if (IsNum (p, 7)) {
  1405.         (void) sscanf (p, "%o", &key);
  1406.         } else {
  1407.         Msg (0,
  1408.             "%s: bind: character, ^x, or octal number expected.", fn);
  1409.         }
  1410.         if (argc < 3) {
  1411.         ktab[key].type = 0;
  1412.         } else {
  1413.         for (pp = KeyNames; *pp; ++pp)
  1414.             if (strcmp (ap[2], *pp) == 0) break;
  1415.         if (*pp) {
  1416.             ktab[key].type = pp-KeyNames+1;
  1417.         } else {
  1418.             ktab[key].type = KEY_CREATE;
  1419.             ktab[key].args = SaveArgs (argc-2, ap+2);
  1420.         }
  1421.         }
  1422.     } else Msg (0, "%s: unknown keyword \"%s\".", fn, ap[0]);
  1423.     }
  1424.     (void) fclose (f);
  1425. }
  1426.  
  1427. static Parse (fn, buf, args) char *fn, *buf, **args; {
  1428.     register char *p = buf, **ap = args;
  1429.     register delim, argc = 0;
  1430.  
  1431.     argc = 0;
  1432.     for (;;) {
  1433.     while (*p && (*p == ' ' || *p == '\t')) ++p;
  1434.     if (*p == '\0' || *p == '#')
  1435.         return argc;
  1436.     if (argc > MAXARGS-1)
  1437.         Msg (0, "%s: too many tokens.", fn);
  1438.     delim = 0;
  1439.     if (*p == '"' || *p == '\'') {
  1440.         delim = *p; *p = '\0'; ++p;
  1441.     }
  1442.     ++argc;
  1443.     *ap = p; ++ap;
  1444.     while (*p && !(delim ? *p == delim : (*p == ' ' || *p == '\t')))
  1445.         ++p;
  1446.     if (*p == '\0') {
  1447.         if (delim)
  1448.         Msg (0, "%s: Missing quote.", fn);
  1449.         else
  1450.         return argc;
  1451.     }
  1452.     *p++ = '\0';
  1453.     }
  1454. }
  1455.  
  1456. static char **SaveArgs (argc, argv) register argc; register char **argv; {
  1457.     register char **ap, **pp;
  1458.  
  1459.     if ((pp = ap = (char **)malloc ((argc+1) * sizeof (char **))) == 0)
  1460.     Msg (0, "Out of memory.");
  1461.     while (argc--) {
  1462.     if ((*pp = malloc (strlen (*argv)+1)) == 0)
  1463.         Msg (0, "Out of memory.");
  1464.     strcpy (*pp, *argv);
  1465.     ++pp; ++argv;
  1466.     }
  1467.     *pp = 0;
  1468.     return ap;
  1469. }
  1470.  
  1471. static MakeNewEnv () {
  1472.     register char **op, **np = NewEnv;
  1473.     static char buf[MAXSTR];
  1474.  
  1475.     if (strlen (SockName) > MAXSTR-5)
  1476.     SockName = "?";
  1477.     sprintf (buf, "STY=%s", SockName);
  1478.     *np++ = buf;
  1479.     *np++ = Term;
  1480.     np += 2;
  1481.     for (op = environ; *op; ++op) {
  1482.     if (np == NewEnv + MAXARGS - 1)
  1483.         break;
  1484.     if (!IsSymbol (*op, "TERM") && !IsSymbol (*op, "TERMCAP")
  1485.         && !IsSymbol (*op, "STY"))
  1486.         *np++ = *op;
  1487.     }
  1488.     *np = 0;
  1489. }
  1490.  
  1491. static IsSymbol (e, s) register char *e, *s; {
  1492.     register char *p;
  1493.     register n;
  1494.  
  1495.     for (p = e; *p && *p != '='; ++p) ;
  1496.     if (*p) {
  1497.     *p = '\0';
  1498.     n = strcmp (e, s);
  1499.     *p = '=';
  1500.     return n == 0;
  1501.     }
  1502.     return 0;
  1503. }
  1504.  
  1505. /*VARARGS2*/
  1506. Msg (err, fmt, p1, p2, p3, p4, p5, p6) char *fmt; {
  1507.     char buf[1024];
  1508.     register char *p = buf;
  1509.  
  1510.     if (Detached)
  1511.     return;
  1512.     sprintf (p, fmt, p1, p2, p3, p4, p5, p6);
  1513.     if (err) {
  1514.     p += strlen (p);
  1515.     if (err > 0 && err < sys_nerr)
  1516.         sprintf (p, ": %s", sys_errlist[err]);
  1517.     else
  1518.         sprintf (p, ": Error %d", err);
  1519.     }
  1520.     if (HasWindow) {
  1521.     MakeStatus (buf, curr);
  1522.     } else {
  1523.     printf ("%s\r\n", buf);
  1524.     Kill (AttacherPid, SIGHUP);
  1525.     exit (1);
  1526.     }
  1527. }
  1528.  
  1529. bclear (p, n) char *p; {
  1530.     bcopy (blank, p, n);
  1531. }
  1532.  
  1533. static char *Filename (s) char *s; {
  1534.     register char *p;
  1535.  
  1536.     p = s + strlen (s) - 1;
  1537.     while (p >= s && *p != '/') --p;
  1538.     return ++p;
  1539. }
  1540.  
  1541. static IsNum (s, base) register char *s; register base; {
  1542.     for (base += '0'; *s; ++s)
  1543.     if (*s < '0' || *s > base)
  1544.         return 0;
  1545.     return 1;
  1546. }
  1547.  
  1548. static char *MakeBellMsg (n) {
  1549.     static char buf[MAXSTR];
  1550.     register char *p = buf, *s = BellString;
  1551.  
  1552.     for (s = BellString; *s && p < buf+MAXSTR-1; s++)
  1553.     *p++ = (*s == '%') ? n + '0' : *s;
  1554.     *p = '\0';
  1555.     return buf;
  1556. }
  1557.  
  1558. static InitUtmp () {
  1559.     struct passwd *p;
  1560.  
  1561.     if ((utmpf = open (UtmpName, O_WRONLY)) == -1) {
  1562.     if (errno != EACCES)
  1563.         Msg (errno, UtmpName);
  1564.     return;
  1565.     }
  1566.     if ((LoginName = getlogin ()) == 0 || LoginName[0] == '\0') {
  1567.     if ((p = getpwuid (getuid ())) == 0)
  1568.         return;
  1569.     LoginName = p->pw_name;
  1570.     }
  1571.     utmp = 1;
  1572. }
  1573.  
  1574. static SetUtmp (name) char *name; {
  1575.     register char *p;
  1576.     register struct ttyent *tp;
  1577.     register slot = 1;
  1578.     struct utmp u;
  1579.  
  1580.     if (!utmp)
  1581.     return 0;
  1582.     if (p = rindex (name, '/'))
  1583.     ++p;
  1584.     else p = name;
  1585.     setttyent ();
  1586.     while ((tp = getttyent ()) != NULL && strcmp (p, tp->ty_name) != 0)
  1587.     ++slot;
  1588.     if (tp == NULL)
  1589.     return 0;
  1590.     strncpy (u.ut_line, p, 8);
  1591.     strncpy (u.ut_name, LoginName, 8);
  1592.     u.ut_host[0] = '\0';
  1593.     time (&u.ut_time);
  1594.     (void) lseek (utmpf, (long)(slot * sizeof (u)), 0);
  1595.     (void) write (utmpf, (char *)&u, sizeof (u));
  1596.     return slot;
  1597. }
  1598.  
  1599. static RemoveUtmp (slot) {
  1600.     struct utmp u;
  1601.  
  1602.     if (slot) {
  1603.     bzero ((char *)&u, sizeof (u));
  1604.     (void) lseek (utmpf, (long)(slot * sizeof (u)), 0);
  1605.     (void) write (utmpf, (char *)&u, sizeof (u));
  1606.     }
  1607. }
  1608.  
  1609. #ifndef GETTTYENT
  1610.  
  1611. static setttyent () {
  1612.     struct stat s;
  1613.     register f;
  1614.     register char *p, *ep;
  1615.  
  1616.     if (ttnext) {
  1617.     ttnext = tt;
  1618.     return;
  1619.     }
  1620.     if ((f = open (ttys, O_RDONLY)) == -1 || fstat (f, &s) == -1)
  1621.     Msg (errno, ttys);
  1622.     if ((tt = malloc (s.st_size + 1)) == 0)
  1623.     Msg (0, "Out of memory.");
  1624.     if (read (f, tt, s.st_size) != s.st_size)
  1625.     Msg (errno, ttys);
  1626.     close (f);
  1627.     for (p = tt, ep = p + s.st_size; p < ep; ++p)
  1628.     if (*p == '\n') *p = '\0';
  1629.     *p = '\0';
  1630.     ttnext = tt;
  1631. }
  1632.  
  1633. static struct ttyent *getttyent () {
  1634.     static struct ttyent t;
  1635.  
  1636.     if (*ttnext == '\0')
  1637.     return NULL;
  1638.     t.ty_name = ttnext + 2;
  1639.     ttnext += strlen (ttnext) + 1;
  1640.     return &t;
  1641. }
  1642.  
  1643. #endif
  1644.  
  1645. #ifdef LOADAV
  1646.  
  1647. static InitKmem () {
  1648.     if ((kmemf = open (KmemName, O_RDONLY)) == -1)
  1649.     return;
  1650.     nl[0].n_name = AvenrunSym;
  1651.     nlist (UnixName, nl);
  1652.     if (nl[0].n_type == 0 || nl[0].n_value == 0)
  1653.     return;
  1654.     avenrun = 1;
  1655. }
  1656.  
  1657. static GetAvenrun () {
  1658.     if (lseek (kmemf, nl[0].n_value, 0) == -1)
  1659.     return 0;
  1660.     if (read (kmemf, loadav, sizeof (loadav)) != sizeof (loadav))
  1661.     return 0;
  1662.     return 1;
  1663. }
  1664.  
  1665. #endif
  1666.  
  1667. #ifndef USEBCOPY
  1668. bcopy (s1, s2, len) register char *s1, *s2; register len; {
  1669.     if (s1 < s2 && s2 < s1 + len) {
  1670.     s1 += len; s2 += len;
  1671.     while (len-- > 0) {
  1672.         *--s2 = *--s1;
  1673.     }
  1674.     } else {
  1675.     while (len-- > 0) {
  1676.         *s2++ = *s1++;
  1677.     }
  1678.     }
  1679. }
  1680. #endif
  1681. SHAR_EOF
  1682. if test 38140 -ne "`wc -c < 'screen.c'`"
  1683. then
  1684.     echo shar: error transmitting "'screen.c'" '(should have been 38140 characters)'
  1685. fi
  1686. fi # end of overwriting check
  1687. echo shar: extracting "'screen.1'" '(21153 characters)'
  1688. if test -f 'screen.1'
  1689. then
  1690.     echo shar: will not over-write existing file "'screen.1'"
  1691. else
  1692. cat << \SHAR_EOF > 'screen.1'
  1693. .if n .ds Q \&"
  1694. .if n .ds U \&"
  1695. .if t .ds Q ``
  1696. .if t .ds U ''
  1697. .TH SCREEN 1 "2 March 1987"
  1698. .UC 4
  1699. .SH NAME
  1700. screen \- screen manager with VT100/ANSI terminal emulation
  1701. .SH SYNOPSIS
  1702. .B screen
  1703. [
  1704. .B \-a
  1705. ] [
  1706. .B \-f
  1707. ] [
  1708. .B \-n
  1709. ] [
  1710. .B \-e\fIxy\fP
  1711. ] [
  1712. .BR \fIcmd args\fP ]
  1713. .br
  1714. .B screen \-r
  1715. [
  1716. .BR \fIhost.tty\fP ]
  1717. .ta .5i 1.8i
  1718. .SH DESCRIPTION
  1719. .I screen
  1720. is a full-screen window manager that
  1721. multiplexes a physical terminal between several processes (typically
  1722. interactive shells).  Each virtual terminal provides the functions
  1723. of the DEC VT100 terminal and, in addition, several control functions
  1724. from the ANSI X3.64 (ISO 6429) and ISO 2022 standards
  1725. (e.\|g. insert/delete line and support for multiple character sets).
  1726. .PP
  1727. When
  1728. .I screen
  1729. is called, it creates a single window with a shell; the pathname of the
  1730. shell is taken from the environment symbol $SHELL; if this is not
  1731. defined, \*Q/bin/sh\*U is used.
  1732. New windows can be created at any time by calling
  1733. .I screen
  1734. from within a previously created window.
  1735. The program to be started in a newly created
  1736. window and optional arguments to the program can be supplied when
  1737. .I screen
  1738. is invoked.
  1739. For instance,
  1740. .IP
  1741. screen csh
  1742. .PP
  1743. will create a window with a C-Shell and switch to that window.
  1744. When the process associated with the currently displayed window
  1745. terminates (e.\|g. ^D has been typed to a shell),
  1746. .I screen
  1747. switches to the previously displayed window;
  1748. when no more windows are left,
  1749. .I screen
  1750. exits.
  1751. .PP
  1752. When \*Q/etc/utmp\*U is writable by
  1753. .IR screen ,
  1754. an appropriate record is written to this file for each window and
  1755. removed when the window is terminated.
  1756. .PP
  1757. The
  1758. .B \-r
  1759. option is used to resume a
  1760. .I screen
  1761. session that has been \fIdetached\fP from the terminal by means
  1762. of the \*Qdetach\*U command key (see below).
  1763. This mechanism allows a user to disconnect
  1764. .I screen
  1765. together with all currently active windows from the terminal
  1766. and resume it at a later point in time, e.\|g. at a later
  1767. login session, and possibly on a different terminal.
  1768. The type of terminal on which a detached
  1769. .I screen
  1770. is resumed must of course be compatible to the type of terminal
  1771. on which
  1772. .I screen
  1773. has originally been invoked.
  1774. When more than one detached
  1775. .I screen
  1776. exist, the
  1777. .B \-r
  1778. option displays a list of
  1779. .I host.tty
  1780. pairs identifying the detached
  1781. .IR screens .
  1782. In this case an additional
  1783. .I host.tty
  1784. argument can be given to resume a specific
  1785. .I screen
  1786. session.
  1787. .SH "COMMAND KEYS"
  1788. The standard way to create a new window is to type \*QC-a c\*U (the notation
  1789. \*QC-x\*U will be used as a shorthand for Control-x in this manual; x is
  1790. an arbitrary letter).
  1791. \*QC-a c\*U creates a new window running a shell and switches to that
  1792. window immediately, regardless of the state of the process running
  1793. in the current window.
  1794. .I Screen
  1795. recognizes several such commands; each command consists of
  1796. \*QC-a\*U followed by a one-letter function.
  1797. For convenience, the letter after a \*QC-a\*U can be entered both with or
  1798. without the control key pressed (with the exception of
  1799. \*QC-a C-a\*U and \*QC-a a\*U; see below), thus, \*QC-a c\*U as well as
  1800. \*QC-a C-c\*U can be used to create a window.
  1801. .PP
  1802. The following commands are recognized by
  1803. .IR screen :
  1804. .IP "\fBC-a c\fP or \fBC-a C-c\fP"
  1805. Create a new window with a shell and switch to that window.
  1806. .IP "\fBC-a k\fP or \fBC-a C-k\fP"
  1807. Kill the current window and switch to the previously displayed window.
  1808. .IP "\fBC-a C-\e\fP"
  1809. Kill all windows and terminate
  1810. .IR screen .
  1811. .IP "\fBC-a d\fP or \fBC-a C-d\fP"
  1812. Detach
  1813. .I screen
  1814. (disconnect it from the terminal and put it into the
  1815. background).
  1816. A detached
  1817. .I screen
  1818. can be resumed by invoking
  1819. .I screen
  1820. with the
  1821. .B \-r
  1822. option.
  1823. .IP "\fBC-a C-a\fP\0\0\0\0\0"
  1824. Switch to the previously displayed window.
  1825. .IP "\fBC-a 0\fP to \fBC-a 9\fP"
  1826. Switch to the window with the number 0 (1, 2, .., 9, respectively).
  1827. When a new window is established, the first available number from the
  1828. range 0..9 is assigned to this window.
  1829. Thus, the first window can be activated by \*QC-a 0\*U; at most
  1830. 10 windows can be present at any time.
  1831. .IP "\fBC-a space\fP or \fBC-a C-space\fP or \fBC-a n\fP or \fBC-a C-n\fP"
  1832. Switch to the next window.  This function can be used repeatedly to
  1833. cycle through the list of windows.
  1834. (Control-space is not supported by all terminals.)
  1835. .IP "\fBC-a p\fP or \fBC-a C-p\fP or \fBC-a -\fP"
  1836. Switch to the previous window (the opposite of \fBC-a space\fP).
  1837. .IP "\fBC-a l\fP or \fBC-a C-l\fP"
  1838. Redisplay the current window.
  1839. .IP "\fBC-a z\fP or \fBC-a C-z\fP"
  1840. Suspend
  1841. .IR screen .
  1842. .IP "\fBC-a h\fP or \fBC-a C-h\fP"
  1843. Write a hardcopy of the current window to the file \*Qhardcopy.\fIn\fP\*U
  1844. in the window's current directory,
  1845. where \fIn\fP is the number of the current window.
  1846. .IP "\fBC-a .\fP (Control-a dot)"
  1847. Write the termcap entry for the virtual terminal of the currently active
  1848. window to the file \*Q.termcap\*U in the directory \*Q$HOME/.screen\*U.
  1849. This termcap entry is identical to the value of the environment symbol
  1850. TERMCAP that is set up by
  1851. .I screen
  1852. for each window.
  1853. .IP "\fBC-a w\fP or \fBC-a C-w\fP"
  1854. Display a list of all windows.
  1855. For each window, the number of the window and the process that has been
  1856. started in the window is displayed; the current window is marked with a
  1857. `*'.
  1858. .IP "\fBC-a t\fP or \fBC-a C-t\fP"
  1859. Print in the message line the time of day, the host name, the load averages
  1860. over 1, 5, and 15 minutes (if this is available on your system),
  1861. the cursor position of the current window in the form \*Q(colum,row)\*U
  1862. starting with \*U(0,0)\*U, an indication if flow control
  1863. and (for the current window)
  1864. insert mode, origin mode, wrap mode, and keypad application
  1865. mode are enabled or not (indicated by a '+' or '-'),
  1866. the currently active character set (\fIG0\fP, \fIG1\fP, \fIG2\fP,
  1867. or \fIG3\fP), and the terminal character sets that are currently
  1868. designated as \fIG0\fP through \fIG3\fP.
  1869. .IP "\fBC-a v\fP or \fBC-a C-v\fP"
  1870. Display the version.
  1871. .IP "\fBC-a a\fP\0\0\0\0\0"
  1872. Send the character \*QC-a\*U to the processes running in the window.
  1873. .IP "\fBC-a s\fP or \fBC-a C-s\fP"
  1874. Send a Control-s to the program running in the window.
  1875. .IP "\fBC-a q\fP or \fBC-a C-q\fP"
  1876. Send a Control-q to the program running in the window.
  1877. .IP
  1878. .PP
  1879. The
  1880. .B \-e
  1881. option can be used to specify a different command character and
  1882. a character which, when typed immediately after the command character,
  1883. generates a literal command character.
  1884. The defaults for these two characters are \*QC-a\*U and `a'.
  1885. (Note that the function to switch to the previous window is actually the
  1886. command character typed twice; for instance, when
  1887. .I screen
  1888. is called with the option \*Q\fB\-e]x\fP\*U (or \*Q\fB\-e ]x\fP\*U),
  1889. this function becomes \*Q]]\*U).
  1890. .SH CUSTOMIZATION
  1891. When
  1892. .I screen
  1893. is invoked, it executes initialization commands from the file
  1894. \*Q.screenrc\*U in the user's home directory.
  1895. Commands in \*Q.screenrc\*U are mainly used to automatically
  1896. establish a number of windows each time
  1897. .I screen
  1898. is called, and to bind functions to specific keys.
  1899. Each line in \*Q.screenrc\*U contains one initialization command; lines
  1900. starting with `#' are ignored.
  1901. Commands can have arguments; arguments are separated by tabs and spaces
  1902. and can be surrounded by single quotes or double quotes.
  1903. .PP
  1904. The following initialization commands are recognized by
  1905. .IR screen :
  1906. .PP
  1907. .ne 3
  1908. .B "escape \fIxy\fP"
  1909. .PP
  1910. Set the command character to \fIx\fP and the character generating a literal
  1911. command character to \fIy\fP (see the \-e option above).
  1912. .PP
  1913. .ne 3
  1914. .B "bell \fImessage\fP"
  1915. .PP
  1916. When a bell character is sent to a background window,
  1917. .I screen
  1918. displays a notification in the message line (see below).
  1919. The notification message can be re-defined by means of the \*Qbell\*U
  1920. command; each occurrence of `%' in \fImessage\fP is replaced by
  1921. the number of the window to which a bell has been sent.
  1922. The default message is
  1923. .PP
  1924.     Bell in window %
  1925. .PP
  1926. An empty message can be supplied to the \*Qbell\*U command to suppress
  1927. output of a message line (bell "").
  1928. .PP
  1929. .ne 3
  1930. .B "mode \fImode\fP"
  1931. .PP
  1932. The mode of each newly allocated pseudo-tty is set to \fImode\fP.
  1933. \fIMode\fP is an octal number.
  1934. When no \*Qmode\*U command is given, mode 0622 is used.
  1935. .PP
  1936. .ne 3
  1937. .B "screen [\fIn\fP] [\fIcmds args\fP]"
  1938. .PP
  1939. Establish a window.
  1940. If an optional number \fIn\fP in the range 0..9 is given, the window
  1941. number \fIn\fP is assigned to the newly created window (or, if this
  1942. number is already in use, the next higher number).
  1943. Note that \fIn\fP has a value of zero for the standard shell window
  1944. created after \*Q.screenrc\*U has been read.
  1945. If a command is specified after \*Qscreen\*U, this command (with the given
  1946. arguments) is started in the window; if no command is given, a shell
  1947. is created in the window.
  1948. Thus, if your \*Q.screenrc\*U contains the lines
  1949. .PP
  1950. .nf
  1951.     # example for .screenrc:
  1952.     screen 1
  1953.     screen 2 telnet foobar
  1954. .fi
  1955. .PP
  1956. .I screen
  1957. creates a shell window (window #1), a window with a TELNET connection
  1958. to the machine foobar (window #2), and, finally, a second shell window
  1959. (the default window) which gets a window number of zero.
  1960. When the initialization is completed,
  1961. .I screen
  1962. always switches to the default window, so window #0 is displayed
  1963. when the above \*Q.screenrc\*U is used.
  1964. .PP
  1965. .ne 3
  1966. .B "chdir [\fIdirectory\fP]"
  1967. .PP
  1968. Change the \fIcurrent directory\fP of
  1969. .I screen
  1970. to the specified directory or, if called without an argument,
  1971. to the home directory (the value of the environment symbol $HOME).
  1972. All windows that are created by means of the \*Qscreen\*U command
  1973. from within \*Q.screenrc\*U or by means of \*QC-a c'' are running
  1974. in the \fIcurrent directory\fP; the \fIcurrent directory\fP is
  1975. initially the directory from which the shell command
  1976. .I screen
  1977. has been invoked.
  1978. Hardcopy files are always written to the directory in which the current
  1979. window has been created (that is, \fInot\fP in the current directory
  1980. of the shell running in the window).
  1981. .PP
  1982. .ne 3
  1983. .B "bind \fIkey\fP [\fIfunction\fP | \fIcmd args\fP]"
  1984. .PP
  1985. Bind a function to a key.
  1986. By default, each function provided by
  1987. .I screen
  1988. is bound to one or more keys as indicated by the above table, e.\|g. the
  1989. function to create a new window is bound to \*QC-c\*U and \*Qc\*U.
  1990. The \*Qbind\*U command can be used to redefine the key bindings and to
  1991. define new bindings.
  1992. The \fIkey\fP
  1993. argument is either a single character, a sequence of the form
  1994. \*Q^x\*U meaning \*QC-x\*U, or an octal number specifying the
  1995. ASCII code of the character.
  1996. If no further argument is given, any previously established binding
  1997. for this key is removed.
  1998. The \fIfunction\fP argument can be one of the following keywords:
  1999. .PP
  2000. .nf
  2001.     shell    Create new window with a shell
  2002.     kill    Kill the current window
  2003.     quit    Kill all windows and terminate
  2004.     detach    Detach \fIscreen\fP
  2005.     other    Switch to previously displayed window
  2006.     next    Switch to the next window
  2007.     prev    Switch to the previous window
  2008.     redisplay    Redisplay current window
  2009.     hardcopy    Make hardcopy of current window
  2010.     termcap    Write termcap entry to $HOME/.screen/.termcap
  2011.     suspend    Suspend \fIscreen\fP
  2012.     windows    Display list of window
  2013.     info    Print useful information in the message line
  2014.     xon    Send Control-q
  2015.     xoff    Send Control-s
  2016.     version    Display the version
  2017.     select0    Switch to window #0
  2018.     \0\0...
  2019.     select9    Switch to window #9
  2020. .fi
  2021. .PP
  2022. In addition, a key can be bound such that a window is created running
  2023. a different command than the shell when that key is pressed.
  2024. In this case, the command optionally followed by
  2025. arguments must be given instead of one of the above-listed keywords.
  2026. For example, the commands
  2027. .PP
  2028. .nf
  2029.     bind ' ' windows
  2030.     bind ^f telnet foobar
  2031.     bind 033 su
  2032. .fi
  2033. .PP
  2034. would bind the space key to the function that displays a list
  2035. of windows (that is, the function usually invoked by \*QC-a C-w\*U
  2036. or \*QC-a w\*U would also be available as \*QC-a space\*U),
  2037. bind \*QC-f\*U to the function \*Qcreate a window with a TELNET
  2038. connection to foobar\*U, and bind \*Qescape\*U to the function
  2039. that creates a window with a super-user shell.
  2040. .SH "VIRTUAL TERMINAL"
  2041. .I Screen
  2042. prints error messages and other diagnostics in a \fImessage line\fP above
  2043. the bottom of the screen.
  2044. The message line is removed when a key is pressed or, automatically,
  2045. after a couple of seconds.
  2046. The message line facility can be used by an application running in
  2047. the current window by means of the ANSI \fIPrivacy message\fP
  2048. control sequence (for instance, from within the shell, something like
  2049. .IP
  2050. echo '^[^Hello world^[\e'   (where ^[ is an \fIescape\fP)
  2051. .PP
  2052. can be used to display a message line.
  2053. .PP
  2054. When the `NF' capability is found in the termcap entry of the
  2055. terminal on which
  2056. .I screen
  2057. has been started, flow control is turned off for the terminal.
  2058. This enables the user to send XON and XOFF characters to the
  2059. program running in a window (this is required by the \fIemacs\fP
  2060. editor, for instance).
  2061. The command line options 
  2062. .B \-n
  2063. and
  2064. .B \-f
  2065. can be used to turn flow control off or on, respectively, independently
  2066. of the `NF' capability.
  2067. .PP
  2068. .I
  2069. Screen
  2070. never writes in the last position of the screen, unless the boolean
  2071. capability `LP' is found in the termcap entry of the terminal.
  2072. Usually,
  2073. .I screen
  2074. cannot predict whether or not a particular terminal scrolls when
  2075. a character is written in the last column of the last line;
  2076. `LP' indicates that it is safe to write in this position.
  2077. Note that the `LP' capability is independent of `am' (automatic
  2078. margins); for certain terminals, such as the VT100, it is reasonable
  2079. to set `am' as well as `LP' in the corresponding termcap entry
  2080. (the VT100 does not move the cursor when a character is written in
  2081. the last column of each line).
  2082. .PP
  2083. .I Screen
  2084. puts into the environment of each process started in a newly created
  2085. window the symbols \*QWINDOW=\fIn\fP\*U (where \fIn\fP is the number
  2086. of the respective window), \*QTERM=screen\*U, and a TERMCAP variable
  2087. reflecting the capabilities of the virtual terminal emulated by
  2088. .IR screen .
  2089. The actual set of capabilities supported by the virtual terminal
  2090. depends on the capabilities supported by the physical terminal.
  2091. If, for instance, the physical terminal does not support standout mode,
  2092. .I screen
  2093. does not put the `so' and `se' capabilities into the window's TERMCAP
  2094. variable, accordingly. 
  2095. However, a minimum number of capabilities must be supported by a
  2096. terminal in order to run
  2097. .IR screen ,
  2098. namely scrolling, clear screen, and direct cursor addressing
  2099. (in addition,
  2100. .I screen
  2101. does not run on hardcopy terminals or on terminals that overstrike).
  2102. .PP
  2103. When the boolean `G0' capability is present in the termcap entry
  2104. for the terminal on which
  2105. .I screen
  2106. has been called, the terminal emulation of
  2107. .I screen
  2108. supports multiple character sets.
  2109. This allows an application to make use of, for instance,
  2110. the VT100 graphics character set or national character sets.
  2111. The following control functions from ISO 2022 are supported:
  2112. \fIlock shift G0\fP (\fISI\fP), \fIlock shift G1\fP (\fISO\fP),
  2113. \fIlock shift G2\fP, \fIlock shift G3\fP, \fIsingle shift G2\fP,
  2114. and \fIsingle shift G3\fP.
  2115. When a virtual terminal is created or reset, the ASCII character
  2116. set is designated as \fIG0\fP through \fIG3\fP.
  2117. .PP
  2118. When the `po' and `pf' capabilities are present in the terminal's
  2119. termcap entry, applications running in a
  2120. .I screen
  2121. window can send output to the printer port of the terminal.
  2122. This allows a user to have an application in one window
  2123. sending output to a printer connected to the terminal, while all
  2124. other windows are still active (the printer port is enabled
  2125. and disabled again for each chunk of output).
  2126. As a side-effect, programs running in different windows can
  2127. send output to the printer simultaneously.
  2128. Data sent to the printer is not displayed in the window.
  2129. .PP
  2130. Some capabilities are only put into the TERMCAP
  2131. variable of the virtual terminal if they can be efficiently
  2132. implemented by the physical terminal.
  2133. For instance, `dl' (delete line) is only put into the TERMCAP
  2134. variable if the terminal supports either delete line itself or
  2135. scrolling regions.
  2136. If
  2137. .I screen
  2138. is called with the
  2139. .B \-a
  2140. option, \fIall\fP capabilities are put into the environment,
  2141. even if
  2142. .I screen
  2143. must redraw parts of the display in order to implement a function.
  2144. .PP
  2145. The following is a list of control sequences recognized by
  2146. .IR screen .
  2147. \*Q(V)\*U and \*Q(A)\*U indicate VT100-specific and ANSI- or
  2148. ISO-specific functions, respectively.
  2149. .PP
  2150. .nf
  2151. .TP 20
  2152. .B "ESC E"
  2153.     Next Line
  2154. .TP 20
  2155. .B "ESC D"
  2156.     Index
  2157. .TP 20
  2158. .B "ESC M"
  2159.     Reverse Index
  2160. .TP 20
  2161. .B "ESC H"
  2162.     Horizontal Tab Set
  2163. .TP 20
  2164. .B "ESC 7"
  2165. (V)    Save Cursor and attributes
  2166. .TP 20
  2167. .B "ESC 8"
  2168. (V)    Restore Cursor and Attributes
  2169. .TP 20
  2170. .B "ESC c"
  2171.     Reset to Initial State
  2172. .TP 20
  2173. .B "ESC ="
  2174. (V)    Application Keypad Mode
  2175. .TP 20
  2176. .B "ESC >"
  2177. (V)    Numeric Keypad Mode
  2178. .TP 20
  2179. .B "ESC # 8"
  2180. (V)    Fill Screen with E's
  2181. .TP 20
  2182. .B "ESC \e"
  2183. (A)    String Terminator
  2184. .TP 20
  2185. .B "ESC ^"
  2186. (A)    Privacy Message (Message Line)
  2187. .TP 20
  2188. .B "ESC P"
  2189. (A)    Device Control String (not used)
  2190. .TP 20
  2191. .B "ESC _"
  2192. (A)    Application Program Command (not used)
  2193. .TP 20
  2194. .B "ESC ]"
  2195. (A)    Operating System Command (not used)
  2196. .TP 20
  2197. .B "Control-N"
  2198. (A)    Lock Shift G1 (SO)
  2199. .TP 20
  2200. .B "Control-O"
  2201. (A)    Lock Shift G0 (SI)
  2202. .TP 20
  2203. .B "ESC n"
  2204. (A)    Lock Shift G2
  2205. .TP 20
  2206. .B "ESC o"
  2207. (A)    Lock Shift G3
  2208. .TP 20
  2209. .B "ESC N"
  2210. (A)    Single Shift G2
  2211. .TP 20
  2212. .B "ESC O"
  2213. (A)    Single Shift G3
  2214. .TP 20
  2215. .B "ESC ( Pcs"
  2216. (A)    Designate character set as G0
  2217. .TP 20
  2218. .B "ESC ) Pcs"
  2219. (A)    Designate character set as G1
  2220. .TP 20
  2221. .B "ESC * Pcs"
  2222. (A)    Designate character set as G2
  2223. .TP 20
  2224. .B "ESC + Pcs"
  2225. (A)    Designate character set as G3
  2226. .TP 20
  2227. .B "ESC [ Pn ; Pn H"
  2228.     Direct Cursor Addressing
  2229. .TP 20
  2230. .B "ESC [ Pn ; Pn f"
  2231.     Direct Cursor Addressing
  2232. .TP 20
  2233. .B "ESC [ Pn J"
  2234.     Erase in Display
  2235. .TP 20
  2236. \h'\w'ESC 'u'Pn = None or \fB0\fP
  2237.     From Cursor to End of Screen
  2238. .TP 20
  2239. \h'\w'ESC 'u'\fB1\fP
  2240.     From Beginning of Screen to Cursor
  2241. .TP 20
  2242. \h'\w'ESC 'u'\fB2\fP
  2243.     Entire Screen
  2244. .TP 20
  2245. .B "ESC [ Pn K"
  2246.     Erase in Line
  2247. .TP 20
  2248. \h'\w'ESC 'u'Pn = None or \fB0\fP
  2249.     From Cursor to End of Line
  2250. .TP 20
  2251. \h'\w'ESC 'u'\fB1\fP
  2252.     From Beginning of Line to Cursor
  2253. .TP 20
  2254. \h'\w'ESC 'u'\fB2\fP
  2255.     Entire Line
  2256. .TP 20
  2257. .B "ESC [ Pn A"
  2258.     Cursor Up
  2259. .TP 20
  2260. .B "ESC [ Pn B"
  2261.     Cursor Down
  2262. .TP 20
  2263. .B "ESC [ Pn C"
  2264.     Cursor Right
  2265. .TP 20
  2266. .B "ESC [ Pn D"
  2267.     Cursor Left
  2268. .TP 20
  2269. .B "ESC [ Ps ;...; Ps m"
  2270.     Select Graphic Rendition
  2271. .TP 20
  2272. \h'\w'ESC 'u'Ps = None or \fB0\fP
  2273.     Default Rendition
  2274. .TP 20
  2275. \h'\w'ESC 'u'\fB1\fP
  2276.     Bold
  2277. .TP 20
  2278. \h'\w'ESC 'u'\fB2\fP
  2279. (A)    Faint
  2280. .TP 20
  2281. \h'\w'ESC 'u'\fB3\fP
  2282. (A)    \fIStandout\fP Mode (ANSI: Italicised)
  2283. .TP 20
  2284. \h'\w'ESC 'u'\fB4\fP
  2285.     Underlined
  2286. .TP 20
  2287. \h'\w'ESC 'u'\fB5\fP
  2288.     Blinking
  2289. .TP 20
  2290. \h'\w'ESC 'u'\fB7\fP
  2291.     Negative Image
  2292. .TP 20
  2293. \h'\w'ESC 'u'\fB22\fP
  2294. (A)    Normal Intensity
  2295. .TP 20
  2296. \h'\w'ESC 'u'\fB23\fP
  2297. (A)    \fIStandout\fP Mode off (ANSI: Italicised off)
  2298. .TP 20
  2299. \h'\w'ESC 'u'\fB24\fP
  2300. (A)    Not Underlined
  2301. .TP 20
  2302. \h'\w'ESC 'u'\fB25\fP
  2303. (A)    Not Blinking
  2304. .TP 20
  2305. \h'\w'ESC 'u'\fB27\fP
  2306. (A)    Positive Image
  2307. .TP 20
  2308. .B "ESC [ Pn g"
  2309.     Tab Clear
  2310. .TP 20
  2311. \h'\w'ESC 'u'Pn = None or \fB0\fP
  2312.     Clear Tab at Current Position
  2313. .TP 20
  2314. \h'\w'ESC 'u'\fB3\fP
  2315.     Clear All Tabs
  2316. .TP 20
  2317. .B "ESC [ Pn ; Pn r"
  2318. (V)    Set Scrolling Region
  2319. .TP 20
  2320. .B "ESC [ Pn I"
  2321. (A)    Horizontal Tab
  2322. .TP 20
  2323. .B "ESC [ Pn Z"
  2324. (A)    Backward Tab
  2325. .TP 20
  2326. .B "ESC [ Pn L"
  2327. (A)    Insert Line
  2328. .TP 20
  2329. .B "ESC [ Pn M"
  2330. (A)    Delete Line
  2331. .TP 20
  2332. .B "ESC [ Pn @"
  2333. (A)    Insert Character
  2334. .TP 20
  2335. .B "ESC [ Pn P"
  2336. (A)    Delete Character
  2337. .TP 20
  2338. .B "ESC [ Ps  ;...; Ps h"
  2339.     Set Mode
  2340. .TP 20
  2341. .B "ESC [ Ps  ;...; Ps l"
  2342.     Reset Mode
  2343. .TP 20
  2344. \h'\w'ESC 'u'Ps = \fB4\fP
  2345. (A)    Insert Mode
  2346. .TP 20
  2347. \h'\w'ESC 'u'\fB?5\fP
  2348. (V)    Visible Bell (\fIOn\fP followed by \fIOff\fP)
  2349. .TP 20
  2350. \h'\w'ESC 'u'\fB?6\fP
  2351. (V)    \fIOrigin\fP Mode
  2352. .TP 20
  2353. \h'\w'ESC 'u'\fB?7\fP
  2354. (V)    \fIWrap\fP Mode
  2355. .TP 20
  2356. .B "ESC [ 5 i"
  2357. (A)    Start relay to printer (ANSI Media Copy)
  2358. .TP 20
  2359. .B "ESC [ 4 i"
  2360. (A)    Stop relay to printer (ANSI Media Copy)
  2361. .fi
  2362. .SH FILES
  2363. .nf
  2364. .ta 2i
  2365. $(HOME)/.screenrc    \fIscreen\fP initialization commands
  2366. .br
  2367. $(HOME)/.screen    Directory created by \fIscreen\fP
  2368. .br
  2369. $(HOME)/.screen/\fIhost.tty\fP    Socket created by \fIscreen\fP
  2370. .br
  2371. hardcopy.[0-9]    Screen images created by the hardcopy function
  2372. .br
  2373. /etc/termcap    Terminal capability data base
  2374. .br
  2375. /etc/utmp    Login records
  2376. .fi
  2377. .SH "SEE ALSO"
  2378. termcap(5), utmp(5)
  2379. .SH AUTHOR
  2380. Oliver Laumann
  2381. .SH BUGS
  2382. Standout mode is not cleared before newline or cursor addressing.
  2383. .PP
  2384. If `LP' is not set but `am' is set, the last character in the last line is never
  2385. written, and it is not correctly re-displayed when the screen is
  2386. scrolled up or when a character is deleted in the last line.
  2387. .PP
  2388. The VT100 \*Qwrap around with cursor addressing\*U bug is not compensated
  2389. when
  2390. .I screen
  2391. is running on a VT100.
  2392. .PP
  2393. `AL,' `DL', and similar parameterized capabilities are not used if present.
  2394. .PP
  2395. `dm' (delete mode), `xn', and `xs' are not handled
  2396. correctly (they are ignored). 
  2397. .PP
  2398. The \fIGR\fP set of ISO 2022 is not supported.
  2399. .PP
  2400. `ms' is not advertised in the termcap entry (in order to compensate
  2401. a bug in
  2402. .IR curses (3X)).
  2403. .PP
  2404. Scrolling regions are only emulated if the physical terminal supports
  2405. scrolling regions.
  2406. .PP
  2407. .I Screen
  2408. does not make use of hardware tabs.
  2409. .PP
  2410. .I Screen
  2411. must be installed as set-uid with owner root in order to be able
  2412. to correctly change the owner of the tty device file for each
  2413. window.
  2414. Special permission may also be required to write the file \*Q/etc/utmp\*U.
  2415. .PP
  2416. Entries in \*Q/etc/utmp\*U are not removed when
  2417. .I screen
  2418. is killed with SIGKILL.
  2419. SHAR_EOF
  2420. if test 21153 -ne "`wc -c < 'screen.1'`"
  2421. then
  2422.     echo shar: error transmitting "'screen.1'" '(should have been 21153 characters)'
  2423. fi
  2424. fi # end of overwriting check
  2425. #    End of shell archive
  2426. exit 0
  2427.  
  2428.  
  2429.